package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;

import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author xioayang
 * @since 2024-06-20
 */

@Slf4j
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper recordMapper;



    /**
     * 添加用户课程
     * @param userId
     * @param courseIds
     */
    @Override
    public void addUserLessons(Long userId, List<Long> courseIds) {
        // 1.查询课程有效期
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);
        if (ObjectUtil.isEmpty(courseInfoList)){
            log.error("课程信息不存在，无法添加到课表");
            return;
        }
        // 2.循环遍历，处理LearningLesson数据
        List<LearningLesson> learningLessonList = new ArrayList<>(courseInfoList.size());
        for (CourseSimpleInfoDTO infoDTO : courseInfoList) {
            //2.1创建实体对象
            LearningLesson learningLesson = new LearningLesson();
            //2.2判断是否已过期
            Integer validDuration = infoDTO.getValidDuration();
            if (validDuration != null && validDuration > 0){
                LocalDateTime now = LocalDateTime.now();
                learningLesson.setCreateTime(now);
                learningLesson.setExpireTime(now.plusMonths(validDuration));
            }
            learningLesson.setCourseId(infoDTO.getId());
            learningLesson.setUserId(userId);
            learningLessonList.add(learningLesson);

        }
        // 3.批量新增
        this.saveBatch(learningLessonList);
    }

    /**
     * 分页查询我的课程
     * @param pageQuery
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> queryLearningLessonPage(PageQuery pageQuery) {
        //已知信息：userId pageQuery   返回信息 userId的LearningLessonVo;
        //根据分页查询可以获得LearningLesson信息 LearningLessonVo-LearningLesson=课程特有的属性；
        //通过远程调用获取课程特有的信息（LearningLesson里有课程id）

        //select * from learning_lesson where user_id = #{userId} order by latest_learn_time desc limit 0 5;
        //1、从线程池里拿到 userId
        Long userId = UserContext.getUser();

        //开启分页 已做
        //2、分页查询
        Page<LearningLesson> page = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        //3、拿到page查询的数据
        List<LearningLesson> records = page.getRecords();
        if (ObjectUtil.isEmpty(records)){
            return PageDTO.empty(page);
        }
        //4、远程调用课程服务，获取课程信息（课程名称、封面、总课时数）
        Map<Long, CourseSimpleInfoDTO> courseMap = queryCourseInfoAndtoMap(records);
        //5、遍历records 将poList封装成voList
        List<LearningLessonVO> voList = new ArrayList<>(records.size());
        for (LearningLesson learningLesson : records) {
            //5.1 po转换vo
            LearningLessonVO vo = BeanUtils.copyBean(learningLesson, LearningLessonVO.class);
            //5.2 获取课程特有的信息（课程名称、封面、总课时数）
            CourseSimpleInfoDTO courseInfoDTO = courseMap.get(learningLesson.getCourseId());
            vo.setCourseCoverUrl(courseInfoDTO.getCoverUrl());
            vo.setCourseName(courseInfoDTO.getName());
            vo.setSections(courseInfoDTO.getSectionNum());
            //5.3 存入集合
            voList.add(vo);

        }
        //6、封装成PageDTO并返回
        return PageDTO.of(page, voList);
    }

    /**
     * 查询正在学习的课程
     *
     * @return
     */
    @Override
    public LearningLessonVO queryLearningLesson() {
        //已知用户id 求LearningLessonVO
        //思路：根据userid获取LearningLesson，根据课程id获取课程信息，封装成LearningLessonVO
        //还缺，最近学习的小节编号，最近学习的小节名，总已报名课程数
        //sql语句：select * from learning_lesson where user_id = #{userId} and status = 1 order by latest_learn_time Desc;

        //1、从线程池里拿到 userId
        Long userId = UserContext.getUser();
        LearningLesson lessonInfo = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        //2、远程调用 根据courseId获取课程信息
        CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(lessonInfo.getCourseId(), false, false);
        if (ObjectUtil.isEmpty(courseInfo)){
            throw new BizIllegalException("课程信息不存在");
        }
        //3、封装成LearningLessonVO
        LearningLessonVO vo = BeanUtils.copyBean(lessonInfo, LearningLessonVO.class);
        vo.setCourseCoverUrl(courseInfo.getCoverUrl());
        vo.setCourseName(courseInfo.getName());
        vo.setSections(courseInfo.getSectionNum());
        //4.统计课表中的课程数量 select count(1) from xxx where user_id = #{userId}
        Integer courseAmount = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .count();
        vo.setCourseAmount(courseAmount);
        //5、获取最近学习的小节编号，最近学习的小节名
        List<CataSimpleInfoDTO> cataInfoDTOS = catalogueClient
                .batchQueryCatalogue(CollUtils.singletonList(lessonInfo.getLatestSectionId()));
        if (ObjectUtil.isNotEmpty(cataInfoDTOS)){
            //获取第一个
            CataSimpleInfoDTO cataInfoDTO = cataInfoDTOS.get(0);
            //存入vo
            vo.setLatestSectionIndex(cataInfoDTO.getCIndex());
            vo.setLatestSectionName(cataInfoDTO.getName());
        }

        return vo;
    }

    /**
     * 删除指定课程信息
     * @param courseId
     */
    @Override
    public void deleteCourseFromLesson(Long userId,Long courseId) {
        //- 用户直接删除已失效的课程（不需要传，直接从threadlocal里哪）
        //- 用户退款后触发课表自动删除（需要传userid）
        if (ObjectUtil.isEmpty(courseId)) {
             userId = UserContext.getUser();
        }

        this.remove(buildUserIdAndCourseIdWrapper(userId, courseId));
    }

    /**
     * 查询课程是否在课程表中
     *
     * @param courseId
     * @return
     */
    @Override
    public Long isLessonValid(Long courseId) {
        //1、获取用户id
       Long userId = UserContext.getUser();
       if (ObjectUtil.isEmpty(userId)){
           throw new BizIllegalException("用户信息不存在");
       }
       //3、查询
        LearningLesson lesson = this.getOne(buildUserIdAndCourseIdWrapper(userId, courseId));

        return lesson.getId();
    }

    /**
     * 根据课程id查询课程
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO queryLearningLessonByCourseId(Long courseId) {
        //1、获取用户id
        Long userId = UserContext.getUser();
        if (ObjectUtil.isEmpty(userId)){
            throw new BizIllegalException("用户信息不存在");
        }
        //2、查询条件
        //3、查询
        LearningLesson lesson = this.getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
        if (ObjectUtil.isEmpty(lesson)){
            return null;
        }
        LearningLessonVO learningLessonVO = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        return learningLessonVO;
    }

    /**
     * 统计课程学习人数
     *
     * @param courseId
     * @return
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        Integer count = lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus,
                        LessonStatus.NOT_BEGIN.getValue(),
                        LessonStatus.LEARNING.getValue(),
                        LessonStatus.FINISHED.getValue())
                .count();
        return count;
    }

    /**
     * 根据用户id和课程id查询课程
     *
     * @param userId
     * @param courseId
     * @return
     */
    @Override
    public LearningLesson queryByUserAndCourseId(Long userId, Long courseId) {
        return getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
    }

    /**
     * 创建学习计划
     *
     * @param planDTO
     */
    @Override
    public void createLearningPlan(LearningPlanDTO planDTO) {
        // 1、查询课表信息
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(UserContext.getUser(), planDTO.getCourseId()));
        AssertUtils.isNotNull(lesson,"课程信息不存在");

        // 2、更新学习计划数据
        this.lambdaUpdate()
                .set(LearningLesson::getWeekFreq, planDTO.getFreq())
                .set(lesson.getPlanStatus() == PlanStatus.NO_PLAN,LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .eq(LearningLesson::getId, lesson.getId())
                .update();
    }

    /**
     * 查询我的学习计划
     *
     * @param pageQuery
     * @return
     */
    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery pageQuery) {
        LearningPlanPageVO vo = new LearningPlanPageVO();
        // 1、获取当前登录用户
        Long userId = UserContext.getUser();

        // 2、分页查询我的课表
        // SELECT * FROM `learning_lesson` WHERE user_id = 2 AND `status` IN (0,1) AND plan_status = 1
        //ORDER BY latest_learn_time DESC LIMIT 0,2;
        Page<LearningLesson> pageResult = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = pageResult.getRecords();
        if(ObjectUtil.isEmpty(records)){
            return vo.emptyPage(pageResult);
        }

        // 3、根据课程ID集合查询课程信息
        Map<Long, CourseSimpleInfoDTO> courseInfoMap = queryCourseInfoAndtoMap(records);

        // 4、统计学习记录中本周学习小节数据
        //SELECT r.`lesson_id`,COUNT(*) AS num  FROM `learning_record` r WHERE r.`user_id` = 2 AND r.`finished` = 1
        // AND r.`finish_time` BETWEEN '2024-06-21 00:00:00' AND '2024-06-22 23:59:59' GROUP BY r.`lesson_id`

        // 4.1、获取本周开始、结束日期
        LocalDateTime begin = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime end = DateUtils.getWeekEndTime(LocalDate.now());

        // 4.2、发起分组统计
        QueryWrapper<LearningRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .select("lesson_id as id,COUNT(*) AS num")
                .eq("user_id",userId)
                .eq("finished",true)
                .between("finish_time",begin,end)
                .groupBy("lesson_id");
        List<Map<String, Object>> mapList = recordMapper.selectMaps(queryWrapper);
        Map<Long, Long> learnedSectionNumMap = mapList.stream().collect(Collectors.toMap(c -> (Long) c.get("id"), c -> (Long) c.get("num")));

        // 5、循环课表数据，进行数据填充补齐
        List<LearningPlanVO> voList = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            // 5.1、拷贝po数据至vo
            LearningPlanVO learningPlanVO = BeanUtils.copyBean(record, LearningPlanVO.class);

            // 5.2、填充课程数据
            CourseSimpleInfoDTO courseInfo = courseInfoMap.get(record.getCourseId());
            if(ObjectUtil.isNotEmpty(courseInfo)){
                learningPlanVO.setCourseName(courseInfo.getName());
                learningPlanVO.setSections(courseInfo.getSectionNum());
            }

            // 5.3、填充本周已学习小节数
            learningPlanVO.setWeekLearnedSections(learnedSectionNumMap.getOrDefault(record.getId(),0L).intValue());

            // 5.4、添加vo至vo集合
            voList.add(learningPlanVO);
        }

        // 6、计算本周计划学习的总小节数（可以直接在mapper层写sql查询，更方便，目前这种写法也要会）
        LearningLesson lesson = this.query()
                .select("sum(week_freq) as total_weekfreq")
                .eq("user_id", userId)
                .eq("plan_status", PlanStatus.PLAN_RUNNING)
                .in("status", LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .one();

        // 7、计算本周已学习的总小节数
        Integer weekFinished = this.recordMapper.selectCount(
                Wrappers.<LearningRecord>lambdaQuery()
                        .eq(LearningRecord::getUserId, userId)
                        .eq(LearningRecord::getFinished, true)
                        .between(LearningRecord::getFinishTime, begin, end)
        );

        // 8、封装Vo并返回
        vo.setWeekTotalPlan(lesson.getTotalWeekfreq());
        vo.setWeekFinished(weekFinished);
        vo.setWeekPoints(null);  //TODO 本周学习积分数

        return vo.pageInfo(pageResult.getTotal(), pageResult.getPages(), voList);
    }





    private Map<Long, CourseSimpleInfoDTO> queryCourseInfoAndtoMap(List<LearningLesson> records) {
        Set<Long> ids = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        List<CourseSimpleInfoDTO> courseList = courseClient.getSimpleInfoList(ids);
        if (ObjectUtil.isEmpty(courseList)){
            throw new BizIllegalException("课程信息不存在");
        }
        Map<Long, CourseSimpleInfoDTO> courseMap = courseList.stream()
                .collect(Collectors.toMap(c -> c.getId(), c -> c));
        return courseMap;
    }


    private LambdaQueryWrapper<LearningLesson> buildUserIdAndCourseIdWrapper(Long userId, Long courseId) {
        LambdaQueryWrapper<LearningLesson> queryWrapper = new QueryWrapper<LearningLesson>()
                .lambda()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        return queryWrapper;
    }
}
